/*
 *--------------------------------------------------------------------------
 *   Battlefield - A Realtime Network Multiplayer Game
 *   =======================================================
 *   Developed by Group D02 - 2009/2010 Semester 4 - CS2103
 *     Harry Nguyen Duy Hoang <nnduyhoang@yahoo.co.uk>
 *     Kent Chng Siang Rong <fivefootway@gmail.com>
 *     Lim Yong Peng <limpeng1986@gmail.com>
 *     Loh Xiankun <u0807185@nus.edu.sg>
 *   Instructed by
 *     Dr. Damith C.Rajapakse <damith@gmail.com>
 *   =======================================================
 *   $Id: MapManager.java 635 2010-07-27 19:15:51Z Kent $
 *   $LastChangedDate: 2010-07-27 19:15:51 +0000 (Tue, 27 Jul 2010) $
 *   $LastChangedBy: Kent $
 *--------------------------------------------------------------------------
 */
package battlefield.manager;

import battlefield.BattlefieldException;
import battlefield.entity.Cell;
import battlefield.entity.CellImage;
import battlefield.entity.Map;
import battlefield.entity.MapCell;
import battlefield.entity.Player;
import battlefield.entity.Unit;
import battlefield.entity.UnitModel;
import battlefield.state.CellState;
import battlefield.util.ArrayUtils;
import battlefield.util.DataUtils;
import battlefield.util.IDGenerator;
import java.util.HashMap;

/**
 *
 * @author Harry Nguyen, Yong Peng, Kent
 *
 * This map manager will contain the configuration of all the
 * maps of the game.
 *
 * When the game requires it will retrieve the list of those map
 * and allows the player to choose from
 */
public class MapManager extends HashMap<Integer, Map> {

    private IDGenerator idGenerator;
    private String[] maps;

    /**
     * Constructs a MapManager Object that will manage all map objects in the
     * server
     */
    public MapManager() {
        idGenerator = new IDGenerator();
        maps = DataUtils.readLines(this.getClass().getResourceAsStream("/battlefield/maps.txt"));
    }

    /**
     * Returns the paths to all the images of the available maps
     * @return
     */
    public String[] getAvailableMaps() {
        return maps;
    }

    /**
     * Creates a Map object for the player. This operation also keeps a copy of
     * the map given to the player.
     * @param player
     * @param name
     * @return Map Object
     * @throws BattlefieldException
     */
    public Map createMap(Player player, String name) throws BattlefieldException {
        if (!ArrayUtils.contains(maps, name)) {
            throw new BattlefieldException("Map name not found.");
        }

        Map map = new Map(idGenerator.next(), Map.WIDTH, Map.HEIGHT, Map.ROWS, Map.COLUMNS);

        for (int i = 0; i < Map.ROWS; i++) {
            for (int j = 0; j < Map.COLUMNS; j++) {
                Cell cell = new MapCell();
                cell.setState(CellState.NORMAL);
                cell.setBackgroundImage(new CellImage(name, j * Cell.WIDTH, i * Cell.HEIGHT, Cell.WIDTH, Cell.HEIGHT));
                cell.setDestroyedImage(new CellImage("/battlefield/images/burnt32x32.png", 0, 0, Cell.WIDTH, Cell.HEIGHT));
                map.setCellAt(cell, i, j);
            }
        }

        put(map.getId(), map);
        player.setMap(map);
        return map;
    }

    /**
     * Check if a unit is allowed to be placed on a position on the map
     * @param map
     * @param model
     * @param x
     * @param y
     * @return boolean
     */
    public boolean isUnitPlaceableOnMap(Map map, UnitModel model, int x, int y) {
        for (int i = 0; i < model.getRows(); i++) {
            for (int j = 0; j < model.getColumns(); j++) {
                if (model.hasCellAt(i, j) && map.hasUnitAt(x + i, y + j) || !map.hasCellAt(x + i, y + j) || model.hasCellAt(i, j) &&map.getCellAt(x + i, y + j).getState() != CellState.NORMAL) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Checks if a weapon is allowed to be placed on the map at the specified x
     * and y position
     * @param map
     * @param weapon
     * @param x
     * @param y
     * @return boolean
     */
    public boolean isWeaponPlaceableOnMap(Map map, Unit weapon, int x, int y) {
        for (int i = 0; i < weapon.getRows(); i++) {
            for (int j = 0; j < weapon.getColumns(); j++) {
                if (weapon.getModel().hasCellAt(i, j) && !map.hasCellAt(x + i, y + j) ||weapon.getModel().hasCellAt(i, j) &&  map.getCellAt(x + i, y + j).getState() == CellState.DESTROYED) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Place a unit on the map at the x and y position
     * @param map
     * @param unit
     * @param x
     * @param y
     * @throws BattlefieldException
     */
    public void placeUnitOnMap(Map map, Unit unit, int x, int y) throws BattlefieldException {

        map.putUnitAt(x, y, unit);
    }

    /**
     * Retrieves a Unit on a specific x and y position on a map and removes it.
     * This operations also returns the Unit retrieved.
     * @param map
     * @param x
     * @param y
     * @return Unit
     * @see Unit
     * @throws BattlefieldException
     */
    public Unit removeUnitOnMap(Map map, int x, int y) throws BattlefieldException {

        if (!map.hasUnitAt(x, y)) {
            throw new BattlefieldException("No unit found.");
        }

        Unit unit = map.getUnitAt(x, y);

        if (unit.isDamaged()) {
            throw new BattlefieldException("Unit is damaged.");
        }

        map.removeUnit(unit);

        return unit;
    }

    /**
     * Checks if a weapon can bomb a specific x and y position on the map
     * @param map
     * @param weapon
     * @param x
     * @param y
     * @return boolean
     * @throws BattlefieldException
     */
    public boolean useWeaponOnMap(Map map, Unit weapon, int x, int y) throws BattlefieldException {
        boolean unitHit = false;
        for (int i = 0; i < weapon.getRows(); i++) {
            for (int j = 0; j < weapon.getColumns(); j++) {
                if (weapon.getModel().hasCellAt(i, j) && !map.hasCellAt(x + i, y + j) || weapon.getModel().hasCellAt(i, j) && map.getCellAt(x + i, y + j).getState() == CellState.DESTROYED) {
                    throw new BattlefieldException("Unit cannot be bombed at (" + x + "," + y + ").");
                }
            }
        }
        for (int i = 0; i < weapon.getRows(); i++) {
            for (int j = 0; j < weapon.getColumns(); j++) {
                if (weapon.hasCellAt(i, j) && map.hasCellAt(x + i, y + j)) {
                    if (map.hasUnitAt(x + i, y + j)) {
                        unitHit = true;
                    }
                    map.getCellAt(x + i, y + j).setState(CellState.DESTROYED);
                }
            }
        }
        return unitHit;
    }
}
